package io.github.libkodi.basic_gateway.context;

import io.github.libkodi.basic_gateway.AbstractVerticle;
import io.github.libkodi.basic_gateway.component.ComponentImpl;
import io.github.libkodi.basic_gateway.router.RouteIntercepterImpl;
import io.github.libkodi.basic_gateway.tools.ClassUtils;
import io.github.libkodi.basic_gateway.tools.ValueUtils;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;

public class ApplicationContext implements Context {
	public static final String CONFIG_NAME = "_config";
	public static final String ROUTE_INTERCEPTER_NAME = "_intercepter";
	
	private Vertx _vertx = null;
	private JsonObject _config = new JsonObject();
	private ComponentImpl _components;
	private RouteIntercepterImpl _intercepter;
	private CurrentThread currentThread = null;

	public ApplicationContext(Vertx vertx) {
		this._vertx = vertx;
		this._components = new ComponentImpl(this);
		this.currentThread = new CurrentThread(this);
	}
	
	@Override
	public Vertx vertx() {
		return _vertx;
	}
	
	@Override
	public JsonObject config() {
		return _config;
	}

	@Override
	public ComponentImpl components() {
		return _components;
	}

	@Override
	public Future<String> deployVerticle(String name) {
		return deployVerticle(name, new DeploymentOptions());
	}

	@Override
	public Future<String> deployVerticle(Class<? extends AbstractVerticle> verticle) {
		return deployVerticle(verticle, new DeploymentOptions());
	}

	@Override
	public Future<String> deployVerticle(AbstractVerticle verticle) {
		return deployVerticle(verticle, new DeploymentOptions());
	}

	@Override
	public Future<String> deployVerticle(String name, DeploymentOptions options) {
		return Future.future(promise -> {
			try {
				Class<?> cls = Class.forName(name);
				Object instance = ClassUtils.newInstance(cls);
				
				if (instance instanceof AbstractVerticle) {
					deployVerticle((AbstractVerticle)instance, options).onComplete(res -> {
						if (res.succeeded()) {
							promise.complete(res.result());
						} else {
							promise.fail(res.cause());
						}
					});
				} else {
					promise.fail(name + " was not inherit " + AbstractVerticle.class.getName());
				}
			} catch (Exception e) {
				promise.fail(e);
			}
		});
	}

	@Override
	public Future<String> deployVerticle(Class<? extends AbstractVerticle> verticle, DeploymentOptions options) {
		return Future.future(promise -> {
			try {
				Object instance = ClassUtils.newInstance(verticle);
				
				deployVerticle((AbstractVerticle)instance, options).onComplete(res -> {
					if (res.succeeded()) {
						promise.complete(res.result());
					} else {
						promise.fail(res.cause());
					}
				});
			} catch (Exception e) {
				promise.fail(e);
			}
		});
	}

	@Override
	public Future<String> deployVerticle(AbstractVerticle verticle, DeploymentOptions options) {
		return Future.future(promise -> {
			ClassUtils.injectContext(verticle, this).onComplete(res -> {
				if (res.failed()) {
					promise.fail(res.cause());
				} else {
					_vertx.deployVerticle(verticle, options).onComplete(ar -> {
						if (ar.succeeded()) {
							promise.complete(ar.result());
						} else {
							promise.fail(ar.cause());
						}
					});
				}
			});
		});
	}

	@Override
	public RouteIntercepterImpl intercepter() {
		return _intercepter;
	}

	@Override
	public Future<JsonObject> config(String keys) {
		return ValueUtils.values(_config, keys);
	}

	@Override
	public <T> T configValue(String key, Class<T> clazz) {
		return ValueUtils.get(_config, key, clazz);
	}

	@Override
	public Object configValue(String key) {
		return ValueUtils.get(_config, key);
	}

	@Override
	public CurrentThread getCurrentThread() {
		return currentThread;
	}
}
